#include "OrbModel.h"

//Include shader manager and effect
#include "ShaderManager.h"
#include "ReflectRefractFX.h"

//Include GeoGen as we are using sphere
#include "GeometryGenerator.h"


OrbModel::OrbModel(ID3D11Device* device)
{
	if (!isInit)
	{
		OutputDebugStringW(L"Loading Orb...\n");

		//Init pointers.
		vb = 0;
		ib = 0;
		textureMap = 0;

		//Obtain effect
		reflectRefractEffect = ShaderManager::GetInstance()->ReturnReflectRefractFX();

		//Createw sphere
		if ( (!vb) && (!ib) )
			LoadGeometryData(device);

		//Load texture
		if (!textureMap)
			HR(D3DX11CreateShaderResourceViewFromFile(device, 
				L"Textures/rock01_diffuse.dds", 0, 0, 
				&textureMap, 0));

		//Set material
		material.Ambient = XMFLOAT4(0.4f, 0.4f, 0.4f, 1.0f);
#ifdef ORB_USE_REFLECTION_SHADER
		material.Diffuse = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f); //Opaque
#else
		material.Diffuse = XMFLOAT4(0.5f, 0.5f, 0.5f, 0.6f); //Semi-transparent.
#endif
		material.Reflection = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
		material.Specular = XMFLOAT4(0.3f, 0.3f, 0.3f, 64.0f);

		//Create bounding box
		GenerateAABB();

		//Class inited
		isInit = true;
	}
	else
		OutputDebugStringW(L"Error - Trying to re-init OrbModel\n");
}

OrbModel::~OrbModel()
{
	//Releae D3D data
	ReleaseCOM(vb);
	ReleaseCOM(ib);

	ReleaseCOM(textureMap);
}

void OrbModel::Render(bool isZPrePass, bool isOpaquePass, bool isShadowPass, 
		VirtualCamera* playerCamera,										 
		DirecLightShadowMapData* shadowMap,									    
		SSAOData* ssaoMap,												       
		ID3D11ShaderResourceView* envMap,									   
		ID3D11ShaderResourceView* llb,                                          
		ID3D11ShaderResourceView* llibO, ID3D11ShaderResourceView* llsebO,     
		ID3D11ShaderResourceView* llibT, ID3D11ShaderResourceView* llsebT,     
		DirectionalLight* dLightArr, unsigned dLightCount,					   
		XMFLOAT3 &hemiColDown, XMFLOAT3 &hemiColRange,					       
		CXMMATRIX &view, CXMMATRIX &proj,									   
		ID3D11DeviceContext* context)
{
	//Terrain is an opaque piece of geometry - only render when we should. 
#ifdef ORB_USE_REFLECTION_SHADER
	if ( ((isOpaquePass) && (isInit)) || (isShadowPass) )
#else
	if ( ((!isOpaquePass) && (isInit)) || (isShadowPass) )
#endif
	{
		//Set the correct technique depending. Note: Not a shadow caster
		//just yet. 
		if (isShadowPass)
			reflectRefractEffect->SetActiveTechniqueByName("ShadowMapRendering");
		else if (isZPrePass)
			reflectRefractEffect->SetActiveTechniqueByName("ZPrePass_AlphaClip");
		else
#ifdef ORB_USE_REFLECTION_SHADER
			reflectRefractEffect->SetActiveTechniqueByName("Texture_AlphaClip_Reflect");
#else
			reflectRefractEffect->SetActiveTechniqueByName("Texture_AlphaClip_Rerfract");
#endif

		//Render
		context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
		context->IASetInputLayout(reflectRefractEffect->ReturnInputLayout());

		UINT stride = sizeof(ReflectRefractFX::PosNormalTexVertex);
		UINT offset = 0;
		context->IASetVertexBuffers(0, 1, &vb, &stride, &offset);
		context->IASetIndexBuffer(ib, DXGI_FORMAT_R32_UINT, 0);

		//Work out global world matrix
		XMMATRIX wd = XMLoadFloat4x4(&worldMatrix);
		XMMATRIX wit = MathHelper::InverseTranspose(wd);
		XMMATRIX wvp = (wd * view) * proj;
		XMMATRIX wv = wd * view;
		//SSAO Transform. 
		XMMATRIX ssaoTrans = wvp * TO_TEX_SPACE;

		//Set variables.
		reflectRefractEffect->SetTextureMap(textureMap);
		reflectRefractEffect->SetEnvironmentMap(envMap);

#ifdef ORB_USE_REFLECTION_SHADER
		reflectRefractEffect->SetLightingData(playerCamera->GetPosition(), dLightArr, dLightCount, 
			llibO, llsebO); //Opaque
#else
		reflectRefractEffect->SetLightingData(playerCamera->GetPosition(), dLightArr, dLightCount, 
			llibT, llsebT); //Semi-transparent
#endif

		reflectRefractEffect->SetHemisphericalData(hemiColDown, hemiColRange);
		reflectRefractEffect->SetSSAOData(ssaoMap->ReturnSSAOMap(), ssaoTrans);

		if ((!isShadowPass) && (shadowMap))
		{
			reflectRefractEffect->SetShadowMap(shadowMap->GetShadowMapSRV());
			reflectRefractEffect->SetShadowMapMatricies(shadowMap->GetLightView(), shadowMap->GetLightProj(), shadowMap->GetNDCToTex());
		}
		else
		{
			XMMATRIX i = XMMatrixIdentity();
			reflectRefractEffect->SetShadowMap(0);
			reflectRefractEffect->SetShadowMapMatricies(i, i, i);
		}

		//Per Object CB
		reflectRefractEffect->SetCBufferPerObject(wd,
			wit, 
			wvp,
			XMMatrixIdentity(),
			material,
			wv);
		

		//Draw
		for (unsigned p = 0; p < reflectRefractEffect->ReturnActiveTechniqueNumberOfPasses(); p++)
		{
			reflectRefractEffect->ApplyActiveTechniqueForPass(p, context);
			context->DrawIndexed(indexCount, 0, 0);
		}

		//Unbind resources.
		ID3D11ShaderResourceView* srvNULL[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0};
		context->PSSetShaderResources(0, 12, srvNULL);
	}
}

void OrbModel::LoadGeometryData(ID3D11Device* device)
{
	//Create sphere
	GeometryGenerator::MeshData data;
	GeometryGenerator geoGen;

	geoGen.CreateGeosphere(3.5f, 3, data);

	//Extract vertex data
	std::vector<ReflectRefractFX::PosNormalTexVertex>verts(data.Vertices.size());

	for (unsigned i = 0; i < data.Vertices.size(); i++)
	{
		verts[i].PosL = data.Vertices[i].Position;
		verts[i].Tex = data.Vertices[i].TexC;
		verts[i].NormalL = data.Vertices[i].Normal;

		//System memory copy. 
		sysCopyVerts.push_back(data.Vertices[i].Position);
	}

	//Create VB.
	D3D11_BUFFER_DESC bd;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.ByteWidth = sizeof(ReflectRefractFX::PosNormalTexVertex) * data.Vertices.size();
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	bd.StructureByteStride = 0;
	bd.Usage  = D3D11_USAGE_IMMUTABLE;

	D3D11_SUBRESOURCE_DATA sd;
	sd.pSysMem = &verts[0];

	//Alloc and init VRAM
	HR(device->CreateBuffer(&bd, &sd, &vb));

	//Create IB
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.ByteWidth = sizeof(UINT) * data.Indices.size();
	
	sd.pSysMem = &data.Indices[0];

	//Alloc and init VRAM
	HR(device->CreateBuffer(&bd, &sd, &ib));

	//Store index count
	indexCount = data.Indices.size();
}